#include <csignal>
#include <chrono>

#include <ppconsul/agent.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/MySQLResult.h>
#include <nlohmann/json.hpp>

#include "UserSign.srpc.h"
#include "CryptoUtil.h"

using namespace std;
using namespace srpc;
using namespace protocol;
using ppconsul::Consul;
using namespace ppconsul::agent;

using Json = nlohmann::json;

// 使用的数据库
static const string g_mysqlURL = "mysql://";

// srpc服务器暴露给外界访问的ip
static const string g_srpcIp = "127.0.0.1";
// srpc服务器监听的端口
static const unsigned short g_srpcPort = 1412;   

// srpc提供的服务名
static const string g_SignService = "UserSignService";

// 注册中心的地址(http://ip:port)
static const string g_consulAddr = "http://localhost:8500";  


static WFFacilities::WaitGroup wait_group(1);

void sig_handler(int signo)
{
	cout << "\tsrpcServer safely shuts down now.\n";
	wait_group.done();
}

// --------------------------------------- 实现服务：注册、登录 ---------------------------------------
class UserSignServiceImpl : public UserSign::Service
{
public:
	// 注册模块
	virtual void signup(SignRequest *request, SignupResponse *response, srpc::RPCContext *ctx) override
	{
		// ************************************ 1、解析请求 ************************************
		// 通过Protobuf的getter方法，获取相应字段的数据
		string username = request->username();
        string password = request->password();
		
		// *********************************** 2、实现业务逻辑 **********************************
		// 2-1. 生成盐值，计算hash(盐值+密码)
        string salt = CryptoUtil::generate_salt();
        string hashcode = CryptoUtil::hash_password(password, salt);
        // 2-2. 拼接sql语句（INSERT）
        string sql = "INSERT INTO tbl_user (username, password, salt) VALUES('" 
                   + username + "', '" + hashcode + "', '" + salt + "');";
        // 2-3. 创建mysql任务
        // 在mysql任务的回调函数中进行rpc响应设置
		WFMySQLTask* mysqlTask = WFTaskFactory::create_mysql_task(g_mysqlURL, 2, 
			[response](WFMySQLTask* task){
				// 1、判断任务执行状态
				if (task->get_state() != WFT_STATE_SUCCESS) {
					response->set_status_code(500);
					response->set_message("SRPC Server SqlTask faild");
					cerr << WFGlobal::get_error_string(task->get_state(), task->get_error()) << endl;
					return;
				}

				// 2、判断sql语句执行情况
				MySQLResponse* resp = task->get_resp();
				if (resp->get_packet_type() == MYSQL_PACKET_ERROR) { // sql执行失败
					response->set_status_code(400);
					response->set_message("The username already exists");
					return ;
				}

				// 3、sql执行成功
				response->set_status_code(200);
				response->set_message("SUCCESS");
			});
    	mysqlTask->get_req()->set_query(sql);  // 设置要执行的sql语句

		// 2-4将mysql任务加入当前rpc服务的串行流
		ctx->get_series()->push_back(mysqlTask);

		cout << "SignUpRequest arived." << endl
			 << "username: " << username << endl << endl;
	}


	// 登录模块
	virtual void signin(SignRequest *request, SigninResponse *response, srpc::RPCContext *ctx) override
	{
		// ************************************ 1、解析请求 ************************************
		// 通过Protobuf的getter方法，获取相应字段的数据
		string username = request->username();
        string password = request->password();
		
		// *********************************** 2、实现业务逻辑 **********************************
		// 2-1. 拼接sql语句（SELECT *）
        string sql = "SELECT * FROM tbl_user WHERE username = '" + username 
                   + "' and tomb = 0;";
        // 2-2. 创建mysql任务
        // 在mysql任务的回调函数中进行rpc响应设置
		WFMySQLTask* mysqlTask = WFTaskFactory::create_mysql_task(g_mysqlURL, 2, 
			[response, password](WFMySQLTask* task){
				// a、判断任务执行状态、sql执行状态
				if (task->get_state() != WFT_STATE_SUCCESS || 
					task->get_resp()->get_packet_type() == MYSQL_PACKET_ERROR) {
					response->set_status_code(500);
					response->set_message("SRPC Server SqlTask faild");
					return;
				}

				// b、获取sql执行结果
				MySQLResultCursor cursor { task->get_resp() };
				vector<MySQLCell> record;   // 一条记录（表中的一行）
				User user;
				// b-1. sql查询到数据
				if (cursor.fetch_row(record)) {
					user.id = record[0].as_int();
					user.username = record[1].as_string();
					user.hashcode = record[2].as_string();
					user.salt = record[3].as_string();
					user.createdAt = record[4].as_string(); 

					// 验证哈希密码通过
					if (user.hashcode == CryptoUtil::hash_password(password, user.salt)) {
						// 根据用户信息生成token
						string token = CryptoUtil::generate_token(user);
						// 设置响应要返回的身份验证信息（JSON格式）
						nlohmann::json retJ = {
							{"data", {
									{"Username", user.username},
									{"Token", token},
									{"Location", "/static/view/home.html"}
									}
							}
						};
						response->set_status_code(200);
						response->set_message("OK");
						response->set_authentication(retJ.dump());
						return;
					}
				}
				// b-2. sql查询无果
				response->set_status_code(400);
				response->set_message("Incorrect username or password");
				return;
			});
    	mysqlTask->get_req()->set_query(sql);  // 设置要执行的sql语句

		// 2-3将mysql任务加入当前rpc服务的串行流
		ctx->get_series()->push_back(mysqlTask);

		cout << "SignInRequest arived." << endl
			 << "username: " << username << endl << endl;
	}
};


// -------------------------------------- 定时器任务的回调函数 --------------------------------------
void timer_callback(WFTimerTask* task)
{	
    if (task->get_state() != WFT_STATE_SUCCESS) {
        return;
    }
    
    SeriesWork* series = series_of(task);
    Agent* agent = static_cast<Agent*>(series->get_context());

    // 发送心跳包
    agent->servicePass("signInstance1");
    // 创建下一个定时任务
    WFTimerTask* nextTask = WFTaskFactory::create_timer_task(7, 0, timer_callback); // 定时7s
    series->push_back(nextTask);
}


// --------------------------------------- 主函数：启动服务器 ---------------------------------------
int main()
{
	signal(SIGINT, sig_handler);  // 注册信号
	
	GOOGLE_PROTOBUF_VERIFY_VERSION;

	// 1、构建srpc服务器
	SRPCServer server;

	// 2、添加服务：注册、登录
	UserSignServiceImpl usersign_impl;
	server.add_service(&usersign_impl);

	// 3、启动服务器
	if (server.start(g_srpcPort) == 0) {
		// *********************** 向注册中心注册[用户注册]和[用户登录]服务 ***********************
		// 指定注册中心 Consul 的ip地址，端口和数据中心
        Consul consul { g_consulAddr, ppconsul::kw::dc="dc1" };
        // 创建代理
        Agent agent { consul };
        // 注册服务 
        agent.registerService(
            kw::id = "signInstance1",      // 实例的名字 (一个服务可以有多个实例)
            kw::name = g_SignService,      // 服务的名字
            kw::address = g_srpcIp,	       // 对外界开放访问的ip
            kw::port = g_srpcPort,
            kw::check = TtlCheck(std::chrono::seconds{ 10 }) // TTL = Time To Live
        );
        agent.servicePass("signInstance1"); // 发送第一个心跳包
        
        // 定时发送心跳包（每隔7秒发送一次）
        WFTimerTask* timerTask = WFTaskFactory::create_timer_task(7, 0, timer_callback);

        SeriesWork* series = Workflow::create_series_work(timerTask, nullptr);
        series->set_context(&agent);    // 设置序列的上下文：传递agent
        series->start();

		// 阻塞主线程，保持服务器线程运行
		wait_group.wait();
		server.stop();
	} else {
		cerr << "Server start failed.\n";
	}

	google::protobuf::ShutdownProtobufLibrary();
	return 0;
}


/* 编译指令（测试用）:

g++ srpcServer.cpp UserSign.pb.cpp CryptoUtil.cpp -o srpcServer -lsrpc -llz4 -lsnappy -lprotobuf -lworkflow -lssl -lcrypto -ljwt -lppconsul

*/